#include "xMappingNodeTypeLibrary.h"
#include "vmLibrary.h"
#include "xParameterPack.h"
#include "xMappingNode.h"
#include "xNodeType.h"
#include "xInMappingPort.h"
#include "xOutMappingPort.h"
#include "Exception.h"

#include <string>
#include <cassert>
#include <fstream>

using namespace std ;

#define BLANK " "

int xMappingNodeTypeLibrary::_xmnt_total;

xMappingNodeTypeLibrary* xMappingNodeTypeLibrary::_xmnt =NULL;

/// constructor
xMappingNodeTypeLibrary::xMappingNodeTypeLibrary(void){

	load_vm_descriptors();
	load_internal_mappingnode();
	load_arithmetic_node();	

}
void xMappingNodeTypeLibrary::load_arithmetic_node() {

	int i = _xmnt_total ;

	// arith
	_xmnt_library[i]._name = "math" ;
	_xmnt_library[i]._category = "arith" ;
	_xmnt_library[i]._description = "handle arbitrary formula" ;
	_xmnt_library[i]._idname = "" ;
	_xmnt_library[i]._id = -1 ;
	_xmnt_library[i]._compositebody_mds = "" ;
	_xmnt_library[i].in_para_no = 1 ;
	_xmnt_library[i].out_para_no = 1 ;
	_xmnt_library[i].in_para_fixed = false ;
	_xmnt_library[i].out_para_fixed = true ;

	_xmnt_library[i].in_para_type.push_back("wop") ;
	_xmnt_library[i].in_para_name.push_back("wop") ;
	_xmnt_library[i].in_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].in_default_value.push_back("NULL");
	_xmnt_library[i].in_para_category.push_back(CATEGORY_OTHER);


	_xmnt_library[i].out_para_type.push_back("wop") ;
	_xmnt_library[i].out_para_name.push_back("wop") ;
	_xmnt_library[i].out_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].out_default_value.push_back("NULL") ;
	_xmnt_library[i].out_para_category.push_back(CATEGORY_OTHER);
	_xmnt_total++ ;


}

void xMappingNodeTypeLibrary::load_internal_mappingnode() {
	int i = _xmnt_total ;

	// wop 
	_xmnt_library[i]._name = "wop" ;
	_xmnt_library[i]._category = "basic" ;
	_xmnt_library[i]._hidden = true ;
	_xmnt_library[i]._description = "the variable" ;
	_xmnt_library[i]._idname = "" ;
	_xmnt_library[i]._id = -1 ;
	_xmnt_library[i]._compositebody_mds = "" ;

	_xmnt_library[i].in_para_no = 0 ;
	_xmnt_library[i].out_para_no = 1 ;
	_xmnt_library[i].in_para_fixed = true ;
	_xmnt_library[i].out_para_fixed = true ;

	_xmnt_library[i].out_para_type.push_back("wop") ;
	_xmnt_library[i].out_para_name.push_back("wop") ;
	_xmnt_library[i].out_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].out_default_value.push_back("NULL") ;
	_xmnt_library[i].out_para_category.push_back(CATEGORY_OTHER);

	_xmnt_total++ ;

	// wop collector
	i = _xmnt_total ;
	_xmnt_library[i]._name = "variable_collector" ;
	_xmnt_library[i]._category = "basic" ;
	_xmnt_library[i]._hidden = false ;
	_xmnt_library[i]._description = "the variable collector" ;
	_xmnt_library[i]._idname = "" ;
	_xmnt_library[i]._id = -1 ;
	_xmnt_library[i]._compositebody_mds = "" ;

	_xmnt_library[i].in_para_no = 1 ;
	_xmnt_library[i].out_para_no = 1 ;
	_xmnt_library[i].in_para_fixed = false ;
	_xmnt_library[i].out_para_fixed = true ;

	_xmnt_library[i].in_para_type.push_back("wop") ;
	_xmnt_library[i].in_para_name.push_back("wop") ;
	_xmnt_library[i].in_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].in_default_value.push_back("NULL");
	_xmnt_library[i].in_para_category.push_back(CATEGORY_OTHER);

	_xmnt_library[i].out_para_type.push_back("wops") ;
	_xmnt_library[i].out_para_name.push_back("wops") ;
	_xmnt_library[i].out_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].out_default_value.push_back("NULL") ;
	_xmnt_library[i].out_para_category.push_back(CATEGORY_OTHER);
	_xmnt_total++ ;

	// vm collector
	i = _xmnt_total ;
	_xmnt_library[i]._name = "vm_collector" ;
	_xmnt_library[i]._category = "basic" ;
	_xmnt_library[i]._hidden = false ;
	_xmnt_library[i]._description = "the vm collector" ;
	_xmnt_library[i]._idname = "" ;
	_xmnt_library[i]._id = -1 ;
	_xmnt_library[i]._compositebody_mds = "" ;

	_xmnt_library[i].in_para_no = 1 ;
	_xmnt_library[i].out_para_no = 1 ;
	_xmnt_library[i].in_para_fixed = false ;
	_xmnt_library[i].out_para_fixed = true ;

	_xmnt_library[i].in_para_type.push_back("vm") ;
	_xmnt_library[i].in_para_name.push_back("vm") ;
	_xmnt_library[i].in_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].in_default_value.push_back("NULL");
	_xmnt_library[i].in_para_category.push_back(CATEGORY_OTHER);

	_xmnt_library[i].out_para_type.push_back("vms") ;
	_xmnt_library[i].out_para_name.push_back("vms") ;
	_xmnt_library[i].out_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].out_default_value.push_back("NULL") ;
	_xmnt_library[i].out_para_category.push_back(CATEGORY_OTHER);
	_xmnt_total++ ;


	// gate , each type has a gate
	i = _xmnt_total ;

	for (int k=0; k< T_array_len; k++) {
		_xmnt_library[i]._name = "gate "+ T_array[k] ;
		_xmnt_library[i]._category = "gate" ;
		_xmnt_library[i]._hidden = false ;
		_xmnt_library[i]._description = "the "+ T_array[k] + " gate port that simply passes parameter" ;
		_xmnt_library[i]._idname = "" ;
		_xmnt_library[i]._id = -1 ;
		_xmnt_library[i]._compositebody_mds = "" ;

		_xmnt_library[i].in_para_no = 1 ;
		_xmnt_library[i].out_para_no = 1 ;
		_xmnt_library[i].in_para_fixed = true ;
		_xmnt_library[i].out_para_fixed = true ;

		_xmnt_library[i].in_para_type.push_back(T_array[k]) ;
		_xmnt_library[i].in_para_name.push_back(T_array[k]) ;
		_xmnt_library[i].in_para_class.push_back(CLASS_REQUIRED) ;
		_xmnt_library[i].in_default_value.push_back("NULL");
		_xmnt_library[i].in_para_category.push_back(CATEGORY_OTHER);

		_xmnt_library[i].out_para_type.push_back(T_array[k]) ;
		_xmnt_library[i].out_para_name.push_back(T_array[k]) ;
		_xmnt_library[i].out_para_class.push_back(CLASS_REQUIRED) ;
		_xmnt_library[i].out_default_value.push_back("NULL") ;
		_xmnt_library[i].out_para_category.push_back(CATEGORY_OTHER);
		// ports are setup when the 
		i++ ;
	}
	_xmnt_total += T_array_len ;

	// vmclone
	// use CUBE_UBVM to set up the ports
	vmdescriptor* vmd = vmLibrary::instance().get_vmdescriptor("CUBE_UBVM");

	_xmnt_library[i]._name = "vmclone" ;
	_xmnt_library[i]._category = "tools" ;
	_xmnt_library[i]._hidden = false ;
	_xmnt_library[i]._description = "The VM clone mapping node that has the sample properties as the VM outside the composite" ;
	_xmnt_library[i]._idname = "";
	_xmnt_library[i]._id = -1 ;
	_xmnt_library[i]._compositebody_mds = "" ;

	_xmnt_library[i].in_para_no = vmd->inputParameters.size() ;
	_xmnt_library[i].in_para_fixed = true ;

	for( auto it = vmd->inputParameters.begin(); it != vmd->inputParameters.end(); ++it ) {
		_xmnt_library[i].in_para_type.push_back( it->type );
		_xmnt_library[i].in_para_name.push_back( it->name );
		_xmnt_library[i].in_para_class.push_back( it->klass );
		_xmnt_library[i].in_default_value.push_back( it->defaultValue );
		_xmnt_library[i].in_para_category.push_back( it->category );
	}

	// for each vm, we duplicate all the inport to output except that the first output is a vm type outport
	// now, we set up the first outport as an VM outport
	_xmnt_library[i].out_para_fixed = true ;
	_xmnt_library[i].out_para_no = vmd->outputParameters.size();

	// now copy all the default inport description to outport
	for( auto it = vmd->outputParameters.begin(); it != vmd->outputParameters.end(); ++it ) {
		_xmnt_library[i].out_para_type.push_back( it->type ) ;
		_xmnt_library[i].out_para_name.push_back( it->name ) ;
		_xmnt_library[i].out_para_class.push_back( it->klass ) ;
		_xmnt_library[i].out_default_value.push_back( it->defaultValue ) ;
		_xmnt_library[i].out_para_category.push_back( it->category );
	}
	_xmnt_total ++ ;

	//string composer
	i = _xmnt_total;
	_xmnt_library[i]._name = "string_composer";
	_xmnt_library[i]._category = "auxiliary";
	_xmnt_library[i]._hidden = false ;
	_xmnt_library[i]._description = "string composer" ;
	_xmnt_library[i]._idname = "" ;
	_xmnt_library[i]._id = -1 ;
	_xmnt_library[i]._compositebody_mds = "" ;

	_xmnt_library[i].in_para_no = 1 ;
	_xmnt_library[i].out_para_no = 1 ;
	_xmnt_library[i].in_para_fixed = false ;
	_xmnt_library[i].out_para_fixed = true ;

	_xmnt_library[i].in_para_type.push_back("tostring*") ;
	_xmnt_library[i].in_para_name.push_back("tostring*") ;
	_xmnt_library[i].in_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].in_default_value.push_back("NULL");	
	_xmnt_library[i].in_para_category.push_back(CATEGORY_OTHER);

	_xmnt_library[i].out_para_type.push_back("string*") ;
	_xmnt_library[i].out_para_name.push_back("") ;
	_xmnt_library[i].out_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].out_default_value.push_back("NULL") ;
	_xmnt_library[i].out_para_category.push_back(CATEGORY_OTHER);
	_xmnt_total++ ;

	// clock
	i = _xmnt_total;
	_xmnt_library[i]._name = "clock";
	_xmnt_library[i]._category = "auxiliary";
	_xmnt_library[i]._hidden = false ;
	_xmnt_library[i]._description = "The animation mapping node" ;
	_xmnt_library[i]._idname = "" ;
	_xmnt_library[i]._id = -1 ;
	_xmnt_library[i]._compositebody_mds = "" ;

	_xmnt_library[i].in_para_no = 4 ;
	_xmnt_library[i].out_para_no = 1 ;
	_xmnt_library[i].in_para_fixed = true ;
	_xmnt_library[i].out_para_fixed = true ;

	_xmnt_library[i].in_para_type.push_back("int*") ;
	_xmnt_library[i].in_para_name.push_back("low") ;
	_xmnt_library[i].in_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].in_default_value.push_back("1");
	_xmnt_library[i].in_para_category.push_back(CATEGORY_OTHER);

	_xmnt_library[i].in_para_type.push_back("int*") ;
	_xmnt_library[i].in_para_name.push_back("high") ;
	_xmnt_library[i].in_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].in_default_value.push_back("NULL");
	_xmnt_library[i].in_para_category.push_back(CATEGORY_OTHER);

	_xmnt_library[i].in_para_type.push_back("int*") ;
	_xmnt_library[i].in_para_name.push_back("increment") ;
	_xmnt_library[i].in_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].in_default_value.push_back("10");
	_xmnt_library[i].in_para_category.push_back(CATEGORY_OTHER);

	_xmnt_library[i].in_para_type.push_back("double*") ;
	_xmnt_library[i].in_para_name.push_back("time_interval") ;
	_xmnt_library[i].in_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].in_default_value.push_back("0.5");
	_xmnt_library[i].in_para_category.push_back(CATEGORY_OTHER);

	_xmnt_library[i].out_para_type.push_back("int*") ;
	_xmnt_library[i].out_para_name.push_back("variant") ;
	_xmnt_library[i].out_para_class.push_back(CLASS_REQUIRED) ;
	_xmnt_library[i].out_default_value.push_back("NULL") ;
	_xmnt_library[i].out_para_category.push_back(CATEGORY_OTHER);
	_xmnt_total++ ;

	// existing VM
	i = _xmnt_total;
	_xmnt_library[i]._name = "existing" ;
	_xmnt_library[i]._category = "dontcare" ;
	_xmnt_library[i]._hidden = true ;
	_xmnt_library[i]._description = "..." ;
	_xmnt_library[i]._idname = "EXISTING";
	_xmnt_library[i]._id = -1 ;
	_xmnt_library[i]._compositebody_mds = "" ;

	_xmnt_library[i].in_para_type.push_back( "wop" );
	_xmnt_library[i].in_para_name.push_back( "object" ) ;
	_xmnt_library[i].in_para_class.push_back( CLASS_REQUIRED ) ;
	_xmnt_library[i].in_default_value.push_back( "NULL" ) ;
	_xmnt_library[i].in_para_category.push_back( CATEGORY_OTHER );
	_xmnt_library[i].in_para_no = 1;
	_xmnt_library[i].in_para_fixed = true ;

	_xmnt_library[i].out_para_type.push_back( "vm" ) ;
	_xmnt_library[i].out_para_name.push_back( "vm" ) ;
	_xmnt_library[i].out_para_class.push_back( CLASS_REQUIRED ) ;
	_xmnt_library[i].out_default_value.push_back( "NULL" ) ;
	_xmnt_library[i].out_para_category.push_back( CATEGORY_OTHER );
	_xmnt_library[i].out_para_no = 1;
	_xmnt_library[i].out_para_fixed = true ;
	_xmnt_total ++ ;
}
void xMappingNodeTypeLibrary::load_vm_descriptors() {

	int total = vmLibrary::instance().get_vm_total();

	for (int i=0; i< total ; i++) {
		vmdescriptor *vm = vmLibrary::instance().get_vmdescriptor(i);


		_xmnt_library[i]._name = vm->vm_name ;
		_xmnt_library[i]._category = vm->vm_category ;
		_xmnt_library[i]._hidden = false ;
		_xmnt_library[i]._description = vm->vm_description ;
		_xmnt_library[i]._idname = vm->idname ;
		_xmnt_library[i]._id = vm->id ;
		_xmnt_library[i]._compositebody_mds = vm->compositebody_mds ;
		_xmnt_library[i].in_para_no = vm->inputParameters.size();
		_xmnt_library[i].in_para_fixed = true ;

		for( auto it = vm->inputParameters.begin(); it != vm->inputParameters.end(); ++it ) {
			_xmnt_library[i].in_para_type.push_back( it->type );
			_xmnt_library[i].in_para_name.push_back( it->name );
			_xmnt_library[i].in_para_class.push_back( it->klass );
			_xmnt_library[i].in_default_value.push_back( it->defaultValue );
			_xmnt_library[i].in_para_category.push_back( it->category );
		}

		// for each vm, we duplicate all the inport to output except that the first output is a vm type outport
		// now, we set up the first outport as an VM outport
		_xmnt_library[i].out_para_fixed = true ;
		_xmnt_library[i].out_para_no = vm->outputParameters.size();

		// now copy all the default inport description to outport
		for( auto it = vm->outputParameters.begin(); it != vm->outputParameters.end(); ++it ) {
			_xmnt_library[i].out_para_type.push_back( it->type ) ;
			_xmnt_library[i].out_para_name.push_back( it->name ) ;
			_xmnt_library[i].out_para_class.push_back( it->klass ) ;
			_xmnt_library[i].out_default_value.push_back( it->defaultValue ) ;
			_xmnt_library[i].out_para_category.push_back( it->category );
		}
	}
	_xmnt_total=total;
}
/// destructor
xMappingNodeTypeLibrary::~xMappingNodeTypeLibrary(void){
}

/// singleton method 
xMappingNodeTypeLibrary* xMappingNodeTypeLibrary::getInstance(){
	if (_xmnt == NULL) 
		_xmnt = new xMappingNodeTypeLibrary();
	return _xmnt;
}

xMappingNodeTypeDescriptor* xMappingNodeTypeLibrary::get_xmnt_descriptor(int i) {
	assert(i < _xmnt_total) ;
	return &(_xmnt_library[i]) ;
}

xMappingNodeTypeDescriptor* xMappingNodeTypeLibrary::get_xmnt_descriptor(string name) {

	int i = get_xmnt_descriptor_index(name);
	if( i < 0 ) {
		throw Exception( "this DES file has not been imported yet", __FILE__, __LINE__ );
	}
	return &(_xmnt_library[i]) ;

}
int xMappingNodeTypeLibrary::get_xmnt_descriptor_index(string name) {
	for (int i=0; i< _xmnt_total; i++){
		if(_xmnt_library[i]._name == name)
			return i ;
	}
	return -1;
}
void xMappingNodeTypeLibrary::add_compositevm_from_xMappingTree(string vmname, string category, string description, xMappingTree* xmt, bool hidden) {

	xmt->compute_level();

	// initialization
	int i = _xmnt_total ;
	_xmnt_library[i]._name = vmname ;
	_xmnt_library[i]._category = category ;
	_xmnt_library[i]._hidden = hidden ;
	_xmnt_library[i]._description = description ;
	_xmnt_library[i]._idname = vmname;
	_xmnt_library[i]._id = -1 ;
	_xmnt_library[i]._compositebody_mds = xmt->toMDString() ;



	vector<xMappingNode*> level0_nodes = xmt->get_nodes_by_level(0);
	assert(level0_nodes.size() >= 1);

	// find all the gates in level 0 and make their inports as the inports of this composite VM
	for (unsigned int j=0; j< level0_nodes.size(); j++) {
		xNodeType *nt = level0_nodes[j]->getNodeTypePtr();
		assert(nt->getType() == "gate"); // the mapping node at level 0 should be of "gate" type

		xInMappingPort *inp = level0_nodes[j]->getInPort(0);
		_xmnt_library[i].in_para_type.push_back(inp->getType()) ;
		_xmnt_library[i].in_para_name.push_back(nt->getName()) ;
		_xmnt_library[i].in_para_class.push_back(CLASS_REQUIRED) ;
		_xmnt_library[i].in_default_value.push_back("NULL") ;
		_xmnt_library[i].in_para_category.push_back(CATEGORY_OTHER);
	}
	_xmnt_library[i].in_para_no = level0_nodes.size();
	_xmnt_library[i].in_para_fixed = true ;


	// In arbitrary cases, the root can be arbitrary mapping node

	_xmnt_library[i].out_para_fixed = true ;
	auto outports = xmt->get_output_nodes();
	_xmnt_library[i].out_para_no = outports.size();

	for( auto it = outports.begin(); it != outports.end(); ++it ) {
		xOutMappingPort* outp = ( *it )->getOutPort( 0 );

		_xmnt_library[i].out_para_type.push_back(outp->getType()) ;
		_xmnt_library[i].out_para_name.push_back(outp->getName()) ;
		_xmnt_library[i].out_para_class.push_back(outp->getClass()) ;
		_xmnt_library[i].out_default_value.push_back(outp->getDefaultValue()) ;
		_xmnt_library[i].out_para_category.push_back(outp->getCategory());
	}
	_xmnt_library[i].in_para_no = level0_nodes.size();

	_xmnt_total++ ;
}

void xMappingNodeTypeLibrary::add_compositevm_from_xMappingTree( string vmname, string category, string description, string mds, bool hidden /*= false */ ){

	xMappingTree* xmt=xMappingTree::fromMDStoXMappingTree(mds,NULL);

	this->add_compositevm_from_xMappingTree(vmname,category,description,xmt);
}

void xMappingNodeTypeLibrary::delete_vm_from_xmnt_library( string vmname )
{
	std::vector<xMappingNodeTypeDescriptor> tempLibrbay;
	std::vector<xMappingNodeTypeDescriptor>::iterator itr;
	tempLibrbay.resize(_xmnt_total);

	std::copy( _xmnt_library, _xmnt_library + _xmnt_total, tempLibrbay.begin());

	for ( itr = tempLibrbay.begin(); itr != tempLibrbay.end(); ++itr  )
	{
		if ( itr->_name == vmname )
		{
			tempLibrbay.erase(itr);
			break;
		}
	}

	std::copy( tempLibrbay.begin(), tempLibrbay.end(), _xmnt_library );
	_xmnt_total = _xmnt_total - 1;
	tempLibrbay.resize(_xmnt_total);
}

void xMappingNodeTypeLibrary::add_typemapping_from_xMappingTree( const std::string & type, const std::string & description, xMappingTree * xmt ) {
	xmt->compute_level();

	// initialization
	int i = _xmnt_total ;
	_xmnt_library[i]._name = type ;
	_xmnt_library[i]._category = "tm" ;
	_xmnt_library[i]._hidden = true ;
	_xmnt_library[i]._description = description ;
	_xmnt_library[i]._idname = type;
	_xmnt_library[i]._id = -1 ;
	_xmnt_library[i]._compositebody_mds = xmt->toMDString() ;

	_xmnt_library[i].in_para_type.push_back( "wop" );
	_xmnt_library[i].in_para_name.push_back( "object" ) ;
	_xmnt_library[i].in_para_class.push_back( CLASS_REQUIRED ) ;
	_xmnt_library[i].in_default_value.push_back( "NULL" ) ;
	_xmnt_library[i].in_para_category.push_back( CATEGORY_OTHER );
	_xmnt_library[i].in_para_no = 1;
	_xmnt_library[i].in_para_fixed = true ;

	_xmnt_library[i].out_para_type.push_back( "vm" ) ;
	_xmnt_library[i].out_para_name.push_back( "vm" ) ;
	_xmnt_library[i].out_para_class.push_back( CLASS_REQUIRED ) ;
	_xmnt_library[i].out_default_value.push_back( "NULL" ) ;
	_xmnt_library[i].out_para_category.push_back( CATEGORY_OTHER );
	_xmnt_library[i].out_para_no = 1;
	_xmnt_library[i].out_para_fixed = true ;

	_xmnt_total++ ;
}
